package com.yth.utils.ognl;

import ognl.*;

import java.util.HashSet;
import java.util.Set;

/**
 * @author yutianhong
 * @version 1.0
 * @since 2025/2/25 19:45
 */
public class OgnlHelper {

    public static Object getValue(String expression, Object root) {
        return OgnlCache.getValue(expression, root);
    }

    public static Set<String> extractVariables(String ognlExpression) throws OgnlException {
        Set<String> parameters = new HashSet<>();
        Node rootNode = (Node) Ognl.parseExpression(ognlExpression);
        traverseNode(rootNode, parameters);
        return parameters;
    }

    private static void traverseNode(Node node, Set<String> variables) {
        if (node instanceof ASTProperty) {
            processPropertyNode((ASTProperty) node, variables);
        } else if (node instanceof ASTVarRef) {
            processVarRefNode((ASTVarRef) node, variables);
        } else if (node instanceof ASTChain) {
            // 链式调用
            Node firstChildNode = node.jjtGetChild(0);
            if (firstChildNode instanceof ASTProperty) {
                processPropertyNode((ASTProperty) firstChildNode, variables);
            } else if (firstChildNode instanceof ASTVarRef) {
                processVarRefNode((ASTVarRef) firstChildNode, variables);
            }
            Node secondChildNode = node.jjtGetChild(1);
            if (secondChildNode instanceof ASTProperty) {
                if (((ASTProperty) secondChildNode).isIndexedAccess()) {
                    // 动态索引
                    traverseNode(secondChildNode.jjtGetChild(0), variables);
                }
            } else if (secondChildNode instanceof ASTMethod) {
                // 方法参数
                for (int i = 0; i < secondChildNode.jjtGetNumChildren(); i++) {
                    Node child = secondChildNode.jjtGetChild(i);
                    traverseNode(child, variables);
                }
            } else if (secondChildNode instanceof ASTSelect) {
                // 集合投影
                Set<String> tempVars = new HashSet<>();
                for (int i = 0; i < secondChildNode.jjtGetNumChildren(); i++) {
                    Node child = secondChildNode.jjtGetChild(i);
                    traverseNode(child, tempVars);
                }
                tempVars.remove("this");
                variables.addAll(tempVars);
            }
        } else {
            // ASTStaticMethod, ASTAdd, ASTTest, ASTCtor, ASTMap, ASTKeyValue, ASTAssign
            // 逻辑运算：ASTAnd, ASTOr, ASTNot, ...
            // 布尔运算：ASTGreater, ASTGreaterEq, ASTLess, ASTLessEq, ASTEq, ASTNotEq, ...
            // 递归处理所有子节点
            for (int i = 0; i < node.jjtGetNumChildren(); i++) {
                Node child = node.jjtGetChild(i);
                traverseNode(child, variables);
            }
        }
    }

    private static void processVarRefNode(ASTVarRef childNode, Set<String> variables) {
        // Get the string after the #
        String varName = childNode.toString().substring(1);
        variables.add(varName);
    }

    private static void processPropertyNode(ASTProperty astProperty, Set<String> variables) {
        Node childNode = astProperty.jjtGetChild(0);
        if (childNode instanceof ASTConst) {
            String varName = ((ASTConst) childNode).getValue().toString();
            variables.add(varName);
        }
    }

    private OgnlHelper() {
        // Prevent instantiation
    }
}
